//`timescale 1ns / 100 fs

`include "e203_defines.v"

module cpu_tiny
	(
		input wire CLK_IN,	
        input wire RST_N,
		output wire LED0,        
		output wire LED1,        
		output wire LED2

	);

    reg [25:0] pre_cnt;

	initial
	begin
		pre_cnt=26'b0;
	end		
	
    //PLL, produce sys_clk
    //    
    wire clk_192M;	    
	wire clk_96M;	
		
	PLL clkpll(		
		.refclk (CLK_IN),	
		.reset (~RST_N),	
		.clk0_out (clk_96M),	//96MHz	
		.clk1_out (clk_192M)		//192MHz
	);

    //produce core_clk
    //by prescale sys_clk    
    
    always@(posedge clk_96M or negedge RST_N) begin
          if (~RST_N) begin
            pre_cnt<=0;
          end
          else begin
            pre_cnt<=pre_cnt+1;
          end
        end    
        
    wire core_clk = pre_cnt[22];            
		
    /**		
     * core part    
     */
    wire [31:0] pc_value;
    wire [31:0] instruction;


	
	//
    //Program Counter
    //  
    wire [31:0] pc_r;
    wire [31:0] pc_next;

    //PC as 32 D flip-flops
    //triggered by core_clk
    sirv_gnrl_dfflr #(32) pc_dfflr (1'b1, pc_next, pc_r, core_clk, RST_N);
    assign pc_value = pc_r;        
    
    //ROM part
    //addressed from pc_value
    //provide instruction            

	I_ROM instruction_rom (		
		.addra (pc_value[5:2]),		
		.rsta (1'b0),		
		.clka (clk_192M),
		.doa (instruction)		
	);	
    
    
    //
	//Decoder	    
    //
	wire [`E203_DECINFO_WIDTH-1:0]  dec_info;
	wire dec_rv32;
	wire dec_jal;
	wire dec_rs1x0;
	wire dec_rs2x0;
	wire dec_rs1en;
	wire dec_rs2en;
	wire dec_rdwen;
	wire [4:0] dec_rs1idx;
	wire [4:0] dec_rs2idx;
	wire [4:0] dec_rdidx;
	wire [`E203_XLEN-1:0] dec_imm;
	wire dec_jalr;
	wire dec_bxx;	

	e203_exu_decode u_e203_exu_decode(
	  .i_instr(instruction),
	  .i_pc(`E203_PC_SIZE'b0),
	  .i_prdt_taken(1'b0), 
	  .i_muldiv_b2b(1'b0), 
	  .i_misalgn (1'b0),
	  .i_buserr  (1'b0),
	  .dbg_mode  (1'b0),
	  .dec_rv32(dec_rv32),
	  .dec_rs1x0 (dec_rs1x0),
	  .dec_rs2x0 (dec_rs2x0),
	  .dec_rs1en (dec_rs1en),
	  .dec_rs2en (dec_rs2en),
	  .dec_rdwen (dec_rdwen),
	  .dec_rs1idx(dec_rs1idx),
	  .dec_rs2idx(dec_rs2idx),
	  .dec_rdidx (dec_rdidx),
	  .dec_imm(dec_imm),
	  .dec_jal (dec_jal ),
	  .dec_jalr (dec_jalr ),	  
	  .dec_bxx (dec_bxx ),	  
	  .dec_info  (dec_info )
	);	    
		
    //bxx logic			
    //	
  //bjp group
  wire inst_bjp_group = (dec_info[`E203_DECINFO_GRP] == `E203_DECINFO_GRP_BJP); 
  wire beq_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BEQ];
  wire bne_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BNE];
  wire blt_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BLT];
  wire bgt_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BGT];
  wire bltu_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BLTU];
  wire bgtu_flag = inst_bjp_group & dec_info[`E203_DECINFO_BJP_BGTU];  
    
  //alu group  
  wire inst_alu_group = (dec_info[`E203_DECINFO_GRP] == `E203_DECINFO_GRP_ALU); 
  //lui
  wire lui_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_LUI];
  //auipc
  wire auipc_flag = inst_alu_group & dec_info[`E203_DECINFO_ALU_OP1PC];
  
    //
    //Register File block    
	//
    wire[4:0] rf_rs1idx;
    wire[4:0] rf_rs2idx;
    wire[31:0] rf_rs1data;
    wire[31:0] rf_rs2data;
    wire[4:0] rf_rdidx;
    wire rf_rdwen;
    wire[31:0] rf_rd_data;
    wire[31:0] gpio1out;
    
    //register file module
    zh_regfile_v01 m_zh_regfile(
      .rst_n(RST_N),
      .core_clk(core_clk),

      .i_rf_rs1idx(rf_rs1idx),
      .i_rf_rs2idx(rf_rs2idx),
      .rf_rs1data(rf_rs1data),
      .rf_rs2data(rf_rs2data),
      .i_rf_rdidx(rf_rdidx),
      .i_rf_rdwen(rf_rdwen),
      .i_rf_rd_data(rf_rd_data),
      .gpio1out(gpio1out)
    );

    //retrieve reg data
    assign rf_rs1idx={5{dec_rs1en   }} & dec_rs1idx;
    assign rf_rs2idx={5{dec_rs2en   }} & dec_rs2idx;          
      
         
	//
    //alu block
    //       
    wire[31:0] alu_data_a;
    wire[31:0] alu_data_b;
    wire alu_sub_flag;
    wire alu_add_flag;
    wire alu_enable;
    wire [31:0] rf_rd_data_alu_result;  
 
	zh_exec_alu_v02 m_zh_exec_alu(
        .i_alu_data_a(alu_data_a),
        .i_alu_data_b(alu_data_b),
        .i_alu_add_flag(alu_add_flag),
        .i_alu_sub_flag(alu_sub_flag),
        .i_alu_enable(alu_enable),
        .alu_result(rf_rd_data_alu_result)
    );        

    assign alu_enable = (dec_info[`E203_DECINFO_GRP] == `E203_DECINFO_GRP_ALU);
	assign alu_data_a = rf_rs1data;
//	assign alu_data_b = dec_info[`E203_DECINFO_ALU_OP2IMM] ? dec_imm : rf_rs2data;
	assign alu_data_b = (inst_alu_group & dec_info[`E203_DECINFO_ALU_OP2IMM]) ? dec_imm : rf_rs2data;
    assign alu_add_flag = dec_info[`E203_DECINFO_ALU_ADD];
    assign alu_sub_flag = dec_info[`E203_DECINFO_ALU_SUB];    
 

    //
    //next pc value could be:
    //jal: pc = pc + imm;
    //normal: pc = pc + 4
//    wire [31:0] adder_pc_add_four = pc_r + 32'd4;
//    wire [31:0] adder_pc_add_imm = pc_r + dec_imm;
//    assign pc_next = dec_jal ? adder_pc_add_imm : adder_pc_add_four;    

  wire [31:0] adder_pc_add_four = pc_r + 32'd4;

  wire [31:0] pc_adder_op1 = dec_jalr ? rf_rs1data : pc_r;
  wire [31:0] adder_pc_or_rs1_add_imm = pc_adder_op1 + dec_imm;
  
  //next pc value could be:
  //jal: pc = pc + imm;
  //jalr: pc = i_rf_rs1data + imm;
  //beq: pc = pc + imm if i_alu_equal_flag
  //bne: pc = pc + imm if ~i_alu_equal_flag
  //blt, bltu: pc = pc + imm if i_alu_less_flag
  //bge, bgeu: pc = pc + imm if i_alu_greater_equal_flag
  //ecall, ebreak: not implemented yet
  //mret: pc = mepc, implement later
  //normal: pc = pc + 4  
  
  wire is_equal =  (alu_data_a == alu_data_b);
  wire is_less =  (alu_data_a < alu_data_b);
  wire bxx_in_effrect = (beq_flag & is_equal) |   
                        (bne_flag & (~is_equal)) |   
                        ((blt_flag|bltu_flag) & is_less) |
                        ((bgt_flag|bgtu_flag) & (~is_less));                        
                   
  wire bjp_in_effrect = dec_jal | dec_jalr | bxx_in_effrect;


  assign pc_next = bjp_in_effrect ? adder_pc_or_rs1_add_imm : adder_pc_add_four;  
  
    

        
    //write back result
    assign rf_rdidx=dec_rdidx;
    assign rf_rdwen=dec_rdwen;
//    assign rf_rd_data =  alu_enable ? rf_rd_data_alu_result : 32'b0;        

  //rf_rd_data could be:
  // * alu result: add sub, shift, logical; slt/sltu: 1 or 0
  // * lui/auipc: imm or pc + imm
  // * jal/jalr: pc + 4
  //
  assign rf_rd_data = lui_flag ? dec_imm : 
                 (dec_jal | dec_jalr) ? adder_pc_add_four:
                 (auipc_flag) ? adder_pc_or_rs1_add_imm :
                 alu_enable ? rf_rd_data_alu_result : 32'b0; 
    
	
  //led output
	assign LED0 = ~gpio1out[1];
	assign LED1 = ~gpio1out[0];
 //   assign LED2 = 1'b1;	
  

	
endmodule
